package com.avantis.common.utils.impl;


import com.avantis.common.utils.IRedisCommands;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Redis 操作命令封装
 *
 * @author bolun@goldmantis.com
 * @version 1.0
 */
@Component
public class RedisCommands implements IRedisCommands {
    @Override
    public void setIncrease(final String key, final int liveTime, final byte[] value) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(key.getBytes(), liveTime, value);
                return null;
            }
        });
    }

    /**
     * @see IRedisCommands#set(String, String)
     */
    @Override
    public void set(final String key, final String value) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), value.getBytes());
                return null;
            }
        });
    }

    /**
     * @see IRedisCommands#set(String, String, String)
     */
    @Override
    public void set(final String key, final String value, final String charSet) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    connection.set(key.getBytes(), value.getBytes(charSet));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void setBytes(final String key, final byte[] value) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), value);
                return null;
            }
        });
    }

    /**
     * @see IRedisCommands#setHash(String, String, byte[])
     */
    @Override
    public void setHash(final String key, final String field, final byte[] value) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.hSet(key.getBytes(), field.getBytes(), value);
                return null;
            }
        });
    }

    /**
     * @see IRedisCommands#setHash(String, String, byte[])
     */
    @Override
    public Boolean setHashNX(final String key, final String field, final byte[] value) {
        return (Boolean) writeTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hSetNX(key.getBytes(), field.getBytes(), value);
            }
        });
    }

    /**
     * @see IRedisCommands#get(String)
     */
    @Override
    public byte[] get(final String key) {
        return (byte[]) readTemplate.execute(new RedisCallback() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.get(key.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands#hashGet(String, String)
     */
    @Override
    public byte[] hashGet(final String key, final String field) {
        return (byte[]) readTemplate.execute(new RedisCallback() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hGet(key.getBytes(), field.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands#setExpire(String, int, String)
     */
    @Override
    public void setExpire(final String key, final int liveTime, final String value) {
        writeTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.setEx(key.getBytes(), liveTime, value.getBytes());
                return null;
            }
        });
    }

    /**
     * @see IRedisCommands#exists(String)
     */
    @Override
    public Boolean exists(final String key) {
        return (Boolean) readTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.exists(key.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands#expire(String, int)
     */
    @Override
    public Boolean expire(final String key, final int liveTime) {
        return (Boolean) writeTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.expire(key.getBytes(), liveTime);
            }
        });
    }

    /**
     * @see IRedisCommands#hashDel(String, String)
     */
    @Override
    public Long hashDel(final String key1, final String key2) {
        return writeTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hDel(key1.getBytes(), key2.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands#hashDel(String, String)
     */
    @Override
    public Long del(final String key1) {
        return writeTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(key1.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands#increase(String)
     */
    @Override
    public Long increase(final String key) {
        return writeTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.incr(key.getBytes());
            }
        });
    }

    @Override
    public Set<byte[]> keys(final String pattern){
        return readTemplate.execute(new RedisCallback<Set<byte[]>>() {
            @Override
            public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.keys(pattern.getBytes());
            }
        });
    }

    @Override
    public Long lPush(final String key, final String value) {
        return writeTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.lPush(key.getBytes(), value.getBytes());
            }
        });
    }

    @Override
    public List<byte[]> lRange(final String key, final long start, final long end) {
        return writeTemplate.execute(new RedisCallback<List<byte[]>>() {
            @Override
            public List<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.lRange(key.getBytes(), start, end);
            }
        });
    }

    @Override
    public Boolean zAdd(final String key, final String value, final double score) {
        return writeTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.zAdd(key.getBytes(), score, value.getBytes());
            }
        });
    }

    @Override
    public String[] zRange(final String key, final long start, final long end) {
        return (String[]) readTemplate.execute(new RedisCallback() {
            @Override
            public String[] doInRedis(RedisConnection connection) throws DataAccessException {
                Set<byte[]> r =connection.zRange(key.getBytes(), start, end);
                String[] result = null;
                byte[][] bResult = null;
                if(r != null){
                    result = new String[r.size()];
                    bResult = new byte[r.size()][];
                    r.toArray(bResult);
                    for(int i=0 ;i < bResult.length; i++){
                        result[i] = new String(bResult[i]);
                    }
                }
                return result;
            }
        });
    }

    @Override
    public String[] zRevRange(final String key, final long start, final long end) {
        return (String[]) readTemplate.execute(new RedisCallback() {
            @Override
            public String[] doInRedis(RedisConnection connection) throws DataAccessException {
                Set<byte[]> r =connection.zRevRange(key.getBytes(), start, end);
                String[] result = null;
                byte[][] bResult = null;
                if(r != null){
                    result = new String[r.size()];
                    bResult = new byte[r.size()][];
                    r.toArray(bResult);
                    for(int i=0 ;i < bResult.length; i++){
                        result[i] = new String(bResult[i]);
                    }
                }
                return result;
            }
        });
    }

    @Override
    public Double zScore(final String key, final String value) {
        return (Double) readTemplate.execute(new RedisCallback() {
            @Override
            public Double doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.zScore(key.getBytes(), value.getBytes());
            }
        });
    }

    @Override
    public Long zCard(final String key) {
        return (Long) readTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.zCard(key.getBytes());
            }
        });
    }

    @Override
    public Long sCard(final String key) {
        return (Long) readTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sCard(key.getBytes());
            }
        });
    }

    @Override
    public Long lLen(final String key) {
        return (Long) readTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.lLen(key.getBytes());
            }
        });
    }

    @Override
    public Set<RedisZSetCommands.Tuple> zRangeWithScores(final String key, final long start, final long end) {
        return (Set<RedisZSetCommands.Tuple>) readTemplate.execute(new RedisCallback() {
            @Override
            public Set<RedisZSetCommands.Tuple> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.zRangeWithScores(key.getBytes(), start, end);
            }
        });
    }

    @Override
    public Set<RedisZSetCommands.Tuple> zRevRangeWithScores(final String key, final long start, final long end) {
        return (Set<RedisZSetCommands.Tuple>) readTemplate.execute(new RedisCallback() {
            @Override
            public Set<RedisZSetCommands.Tuple> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.zRevRangeWithScores(key.getBytes(), start, end);
            }
        });
    }

    /**
     * @see IRedisCommands#hashGetAll(String)
     */
    @Override
    public Map<byte[],byte[]> hashGetAll(final String key) {
        return (Map<byte[],byte[]>) readTemplate.execute(new RedisCallback() {
            @Override
            public Map<byte[],byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hGetAll(key.getBytes());
            }
        });
    }

    /**
     * @see IRedisCommands
     */
    @Override
    public List<byte[]> hMGet(final String key, final String ...field) {
        return (List<byte[]>) readTemplate.execute(new RedisCallback() {
            @Override
            public List<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                byte[][] r = new byte[field.length][];
                for(int i=0; i < r.length ; i ++){
                    r[i] = field[i].getBytes();
                }
                return connection.hMGet(key.getBytes(),r);
            }
        });
    }
    
    /**
     * @see IRedisCommands
     */
    @Override
    public Set<byte[]> hKeys(final String key) {
        return (Set<byte[]>) readTemplate.execute(new RedisCallback() {
            @Override
            public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hKeys(key.getBytes());
            }
        });
    }

    @Override
    public Boolean setNx(final String key, final String value){
        return (Boolean) readTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX(key.getBytes(), value.getBytes());
            }
        });
    }

    @Override
    public Long sadd(String key, String value) {
        return (Long) readTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sAdd(key.getBytes(), value.getBytes());
            }
        });
    }

    @Override
    public Long srem(String key, String value) {
        return (Long) readTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sRem(key.getBytes(), value.getBytes());
            }
        });
    }

    @Override
    public Boolean sismember(String key, String value) {
        return (Boolean) readTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sIsMember(key.getBytes(), value.getBytes());
            }
        });
    }

    @Autowired
    @Qualifier("readTemplate")
    protected StringRedisTemplate readTemplate;

    @Autowired
    @Qualifier("writeTemplate")
    protected StringRedisTemplate writeTemplate;
}
